﻿using CKS.Core.Framework.Models;
using CKS.Core.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using CKS.Models;
using FreeSql;
using CKS.AGV.Core.Enum;
using System.Security.Policy;
using Flurl;
using Flurl.Http;
using Mapster;

namespace CKS.AGV.Core
{
    [Schedule]
    public class AgvConveyTaskJob : CronJobBase
    {
        IFreeSql<MesDB> fsql;
        IBaseRepository<agv_convey_tasks> repoTasks;
        IBaseRepository<agv_path_point> repoPoint;
       
        public string hikBaseUrl = "";
        public AgvConveyTaskJob() : base("AGV输送任务服务", "0/1 * * * * ?")
        {
           fsql = AppCache.GetServiceProvider().GetService<IFreeSql<MesDB>>();
            repoTasks = fsql.GetRepository<agv_convey_tasks>();
            repoPoint = fsql.GetRepository<agv_path_point>();
           
            hikBaseUrl = Configs.GetValue("hikRcmsBaseUrl");
        }


        List<agv_path_point> AllPoints = new List<agv_path_point>();
       
        protected override async Task ExecuteAsync(CancellationToken cancellation)
        {


            while (!cancellation.IsCancellationRequested)
            {
                AllPoints= repoPoint.Select.ToList();
               
                await ProcessTasks();
                await Task.Delay(50);
            }


           


            await Task.CompletedTask;
        }

        short LastHeart = 0;
        DateTime lastHeartTime = DateTime.Now;
        bool AgvIsOnline = true;
        
        private async Task ProcessTasks()
        {
            
            ModbusMainService.WriteAgvWord(1, 1);

            var heart = ModbusMainService.ReadUShort(11);
            if(heart != LastHeart)
            {
                lastHeartTime = DateTime.Now;
                if(AgvIsOnline == false)
                {
                    EventBus.AddLog("AGV重新连接成功");
                }
                AgvIsOnline = true;
            }
            //超过3秒代表不正常
            if((DateTime.Now-lastHeartTime).TotalSeconds>3)
            {
                AgvIsOnline = false;
                lastHeartTime = DateTime.Now;
                EventBus.AddLog("AGV心跳静止");
                return;
            }
           



            var RSP = ModbusMainService.ReadUShort(12);
            var REQ = ModbusMainService.ReadUShort(2);

            /*
               查询未完成的任务列表，
                0 默认 1 已下发，2 任务已接收 ， 
                0：-> 向AGV下发任务
                1: 已下发:查询AGV响应状态
                2: 执行中
                
                10: 执行完成
                11:重复任务已取消

                防错：在生成任务的时候，相同任务只允许存在1个。

             */
            //先处理取消操作、在做后续处理
            var cancelTasks = repoTasks.Where(a =>a.CancelState == 1 && (int)a.TaskStatus < 10).OrderBy(a => a.Id).ToList();
            foreach (var item in cancelTasks)
            {
                var hikBaseUrl = Configs.GetValue("hikRcmsBaseUrl");
                Dictionary<string, object> dicparams = new Dictionary<string, object>();
                dicparams.Add("reqCode", $"{Guid.NewGuid().ToString("N")}");
                dicparams.Add("taskCode", item.HikTaskCode);

                

                try
                {
                    var response = await hikBaseUrl.AppendPathSegment("cancelTask")
                   .WithHeader("X-LR-REQUEST-ID", $"{Guid.NewGuid().ToString("N")}") // 添加请求头;
                   .PostJsonAsync(dicparams);
                    var resData = await response.GetStringAsync();
                    var oj = resData.ToJObject();
                    if (oj["code"].ToString().Equals("0") || item.TaskStatus == AgvTaskStatus.创建任务)
                    {
                        item.TaskStatus = AgvTaskStatus.手动取消;
                        item.CancelState = 2;
                        item.EndTime = DateTime.Now;
                        repoTasks.Update(item);

                        //取消任务后任务生成结果重置
                        var plcid = 0;
                        agv_path_point callerPoint = null;
                        if (item.TaskType == 1)
                        {
                            plcid = item.EndPlcId;
                            callerPoint = AgvCache.AllPoints.Find(a => a.Point == item.EndPathPoint);
                        }
                        else if (item.TaskType == 2)
                        {
                            plcid = item.StartPlcId;
                            callerPoint = AgvCache.AllPoints.Find(a => a.Point == item.StartPathPoint);
                        }
                        //如果是叉车任务需要通知两个plc
                        //取消任务暂时不通知，后续根据实际使用情况设计好后再调整
                        if(item.TaskType ==1 || item.TaskType == 2)
                        {
                            var client = AgvCache.PlcClients.ToList().Find(a => a.Id == plcid);
                            client.ResetPlcAgvTaskStatus1(item, callerPoint);
                        }
                       
                        
                    
                    }
                    

                }
                catch (Exception ex)
                {

                }
            }

            var tasks = repoTasks.Where(a => (int)a.TaskStatus < 10).OrderBy(a => a.Id).ToList();

            // 查重并排除 ID 最小的重复项
            var result = tasks
               .GroupBy(a => new { a.UniquePath })
                .Where(group => group.Count() > 1) // 只处理有重复项的分组
                .SelectMany(group => {
                    var f = group.First();
                   return group.Where(a=>a.Id != f.Id).OrderByDescending(item => item.Id); })
               .ToList();
            foreach (var item in result)
            {
                //item.TaskStatus = AgvTaskStatus.因重复取消;
                //item.EndTime = DateTime.Now;
                tasks.Remove(item);
            }
            //repoTasks.Update(result);

           

            if (REQ == 0 && RSP == 0) // 可以下发任务的状态、下发任务时一次只下发一个任务
            {
                /*
                 如果存在任务状态是已下发，可能是程序重启留下的，
                 如果重新下发的话，会导致AGV那边重复，不做处理
                 
                */
                //当前是可以下任务的状态
                //查找请求出料任务 匹配 请求送料任务
                // agv_outflow_task   && MatchOut == true
               
                var task =tasks.Find(a => a.TaskStatus == 0 &&  a.CanSend);
                if(task != null)
                {
                    //开始下发任务
                    //AgvMainService.WriteAgvWord(3,(ushort)task.StartPathPoint);
                    //AgvMainService.WriteAgvWord(4, (ushort)task.EndPathPoint);
                    //if(task.TaskType == 3)
                    //    AgvMainService.WriteAgvWord(5, 1);
                    //AgvMainService.WriteAgvWord(2, 1);

                    var hikTaskType = task.TaskType == 3 ? "T3" : "T1";

                    Dictionary<string,object> dicparams = new Dictionary<string,object>();
                    dicparams.Add("reqCode", $"{Guid.NewGuid().ToString("N")}");
                    dicparams.Add("taskTyp", hikTaskType);
                    dicparams.Add("positionCodePath", new object[] { 
                        new { positionCode = $"{task.StartPathPoint}", type="00" },
                        new { positionCode = $"{task.EndPathPoint}", type="00" }
                    } );
                    if(task.TaskType == 3)
                    {
                        dicparams.Add("ctnrTyp", "1");
                    }

                    try
                    {
                        var response = await hikBaseUrl.AppendPathSegment("genAgvSchedulingTask")
                       .WithHeader("X-LR-REQUEST-ID", $"{Guid.NewGuid().ToString("N")}") // 添加请求头;
                       .PostJsonAsync(dicparams);
                        var resData = await response.GetStringAsync();
                        var oj = resData.ToJObject();
                        if (oj["code"].ToString().Equals("0"))
                        {
                            task.HikTaskCode = oj["data"].ToString();
                            task.TaskStatus = AgvTaskStatus.任务已接收;
                            task.SendHex = $"{task.StartPathPoint} {task.EndPathPoint}";
                            task.SendTime = DateTime.Now;
                            repoTasks.Update(task);
                            EventBus.AddLog($"下发任务 {task.Desc} {task.HikTaskCode}");
                        }
                    }
                    catch (Exception ex)
                    {

                    }
                   
                }
            }
            else if(RSP == 1) //任务下发成功 ，查询已下发状态的任务，
            {
              //如果存在多个已下发，将最后一条改为 任务已接收；其余已下发改为0，可以重新进行下发。
                var one  = tasks.Where(a => a.TaskStatus == AgvTaskStatus.已下发).OrderByDescending(a=>a.SendTime).FirstOrDefault();
                if(one != null)
                {
                    one.TaskStatus = AgvTaskStatus.任务已接收;
                    repoTasks.Update(one);
                    EventBus.AddLog($"任务已接收 {one.Desc}");
                }
                //处理结束、需要重置下发任务的信号
                ModbusMainService.WriteAgvWord(3, 0);
                ModbusMainService.WriteAgvWord(4, 0);
                ModbusMainService.WriteAgvWord(5, 0);
                ModbusMainService.WriteAgvWord(2, 0);
                ModbusMainService.WriteAgvWord(12, 0);
            }

            if (AgvIsOnline == false)
            {
                return;
            }

            var agvtasks = tasks.FindAll(a => a.TaskStatus >= AgvTaskStatus.任务已接收 && (int)a.TaskStatus<10);
            //先更新agv状态
            foreach(var agvtask in agvtasks)
            {
                if(agvtask.TaskStatus == AgvTaskStatus.任务已接收)
                {
                    //取料流程
                    var agvState = (AgvStatus)ModbusMainService.ReadUShort(agvtask.StartReqSignalAddr);
                    if (agvState == AgvStatus.取料申请)
                    {
                        agvtask.LastAgvReqStatus = agvState;
                        //发送允许取料
                        ModbusMainService.WriteAgvWord(agvtask.StartRspSignalAddr, (ushort)ResAgvStatus.取料允许进入);
                        //通知PLC取料
                        var client = AgvCache.PlcClients.ToList().Find(a => a.Id == agvtask.StartPlcId);
                        if (client != null && agvtask.CanNotifyGet)
                        {
                            //只有呼叫取料的任务和叉车任务需要回复PLC取料到达
                            client.NotifyMaterialRetrievalResult(agvtask, AgvArriveStatus.已到达);

                        }
                        agvtask.LastAgvRspStatus = ResAgvStatus.取料允许进入;
                        agvtask.TaskStatus = AgvTaskStatus.已同意取料进入;
                        repoTasks.Update(agvtask);

                        EventBus.AddLog($"任务 {agvtask.Desc},小车到达{agvtask.StartPathPoint},已同意取料进入");
                    }
                }
                else if(agvtask.TaskStatus == AgvTaskStatus.已同意取料进入)
                {
                    // 检测 是否 取料离开，如果是取料任务需要通知PLC小车已离开
                    var agvState = (AgvStatus)ModbusMainService.ReadUShort(agvtask.StartReqSignalAddr);
                    if(agvState == AgvStatus.取料离开)
                    {
                        //通知PLC取料离开，只有取料任务才通知
                        var client = AgvCache.PlcClients.ToList().Find(a => a.Id == agvtask.StartPlcId);
                        if (client != null && agvtask.TaskType == 2)
                        {
                            //只有呼叫取料的任务需要回复PLC料已离开
                            client.NotifyMaterialRetrievalResult(agvtask, AgvArriveStatus.已离开);

                        }
                        agvtask.LastAgvReqStatus = agvState;
                        agvtask.TaskStatus = AgvTaskStatus.取料离开;
                        repoTasks.Update(agvtask);
                        EventBus.AddLog($"任务 {agvtask.Desc},小车离开{agvtask.StartPathPoint},取料离开");
                    }

                }
                else if(agvtask.TaskStatus == AgvTaskStatus.取料离开)
                {
                    //开始前往目的地，因此需要检查目的地的状态
                   var  agvState = (AgvStatus)ModbusMainService.ReadUShort(agvtask.EndReqSignalAddr);
                    if (agvState == AgvStatus.送料申请)
                    {
                        agvtask.LastAgvReqStatus = agvState;
                        //发送允许送料
                        ModbusMainService.WriteAgvWord(agvtask.EndRspSignalAddr, (ushort)ResAgvStatus.送料允许进入);
                        //通知PLC送料已到达
                        var client = AgvCache.PlcClients.ToList().Find(a => a.Id == agvtask.EndPlcId);
                        if (client != null && agvtask.TaskType == 1)
                        {
                            //只有呼叫送料的任务需要回复PLC料 送料到达
                            client.NotifyMaterialDeliveryResult(agvtask,1);

                        }
                        agvtask.LastAgvRspStatus = ResAgvStatus.送料允许进入;
                        agvtask.TaskStatus = AgvTaskStatus.已同意送料进入;
                        repoTasks.Update(agvtask);
                        EventBus.AddLog($"任务 {agvtask.Desc},小车到达{agvtask.EndPathPoint},已同意送料进入");
                    }
                }
                else if(agvtask.TaskStatus == AgvTaskStatus.已同意送料进入 )
                {
                    //终点任务完成
                    var agvState = (AgvStatus)ModbusMainService.ReadUShort(agvtask.EndReqSignalAddr);
                    if(agvState == AgvStatus.送料离开)
                    {
                        agvtask.LastAgvReqStatus = agvState;
                        var client = AgvCache.PlcClients.ToList().Find(a => a.Id == agvtask.EndPlcId);
                        if (client != null && agvtask.CanNotifySend)
                        {
                            //只有呼叫送料的任务需要回复PLC料 送料离开
                            client.NotifyMaterialDeliveryResult(agvtask, 2);
                        }

                        //任务完成，需要重置信号状态
                        ModbusMainService.WriteAgvWord(agvtask.StartReqSignalAddr, (ushort)ResAgvStatus.默认);
                        ModbusMainService.WriteAgvWord(agvtask.StartRspSignalAddr, (ushort)ResAgvStatus.默认);
                        ModbusMainService.WriteAgvWord(agvtask.EndReqSignalAddr, (ushort)ResAgvStatus.默认);
                        ModbusMainService.WriteAgvWord(agvtask.EndRspSignalAddr, (ushort)ResAgvStatus.默认);

                        agvtask.TaskStatus = AgvTaskStatus.完成;
                        agvtask.EndTime = DateTime.Now;
                        repoTasks.Update(agvtask);
                        EventBus.AddLog($"任务 {agvtask.Desc},小车离开{agvtask.EndPathPoint},已完成");
                        //如果是叉车任务，需要更新叉车任务的状态为完结
                        fsql.Update<agvserver_tasks>().Set(a => a.TaskStatus, 10)
                            .Where(a=>a.ConveyId == agvtask.Id).ExecuteAffrows();

                    }

                }
            }


            await Task.CompletedTask;
        }
    }
}
